/*
 * This file is part of the EasyLogger Library.
 *
 * Copyright (c) 2015-2017, Armink, <armink.ztl@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * 'Software'), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Function: linux demo.
 * Created on: 2015-07-30
 */

#define LOG_TAG    "main"
#define ELOG_ASSERT_ENABLE

#include "elog.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>

#define FAIL -1
#define SUCCESS 0
#define DEFAULT_SESSION_ID 5082663
#define SERVERPORT 8554
#define SERVER_RTP_PORT 55532
#define SERVER_RTCP_PORT 55533
#define READ_BUF_SIZE 1024*60
#define ERRINFO strerror(errno)
#define MAX_RTP_PACKET_LENGTH  1400
#define FPS 25
int sendthread;

#pragma pack (1)
typedef struct SessionContext_
{
    struct sockaddr_in ClientAddr;
    int ClientSock;
    int SerRtpSock;
    int SerRtcpSock;
    int ClientRtpPort;
    int ClientRtcpPort;
    unsigned char RequestUrl[100];
} SessionContext;

typedef struct NALUHEADER_
{
   unsigned char NaluType:5;
   unsigned char NRI:2;
   unsigned char F:1;
}NaluHeader;

typedef struct NaluInfo_
{
   NaluHeader PSNaluHeader;
   unsigned char* NaluData;
   unsigned char* origin;
   unsigned int NaluLength;
}NaluInfo;

typedef struct RtpHeader_
{
    unsigned char CsrcCount:4;
    unsigned char Extendion:1;
    unsigned char Padding:1;
    unsigned char Version:2;

    unsigned char PlayLoad:7;
    unsigned char Mark:1;
    
    unsigned short Sequence;
    unsigned int TimeStamp;
    unsigned int SSRC;
}RtpHeader;

typedef struct RtpSigPacket_
{
    RtpHeader SRtpHeader;
    NaluHeader SNaluHeader;
    unsigned char NaluData[READ_BUF_SIZE];
}RtpSigPacket;

typedef struct FU_Indication_
{
    unsigned char PacketType:5; //28
    unsigned char NRI:2;        //Importance level
    unsigned char F:1;          //false
}FU_Indication;

typedef struct FU_Header_
{
    unsigned char NaluType:5;   //nalutype
    unsigned char R:1;          //reserve
    unsigned char E:1;          //end
    unsigned char S:1;          //start
}FU_Header;

typedef struct RtpFU_APacket_
{
    RtpHeader SRtpHeader;
    FU_Indication SFuIndication;
    FU_Header SFuHeader;
    unsigned char NaluData[READ_BUF_SIZE];
}RtpFU_APacket;
#pragma pack()

void BuildNaluInfo(unsigned char* buf, unsigned int nallength, NaluInfo* naluinfo)
{
    ELOG_ASSERT(buf != NULL && naluinfo != NULL);
    naluinfo->origin = buf;
    naluinfo->PSNaluHeader.F = (*buf & 0x80) >> 7;
    naluinfo->PSNaluHeader.NRI = (*buf & 0x60) >> 5;
    naluinfo->PSNaluHeader.NaluType = *buf & 0x1F;
    naluinfo->NaluData = buf + 1;
    naluinfo->NaluLength = nallength;
}

static void LogModuleInit()
{
    /* close printf buffer */
    setbuf(stdout, NULL);
    /* initialize EasyLogger */
    elog_init();
    /* set EasyLogger log format */
    elog_set_fmt(ELOG_LVL_ASSERT, ELOG_FMT_ALL);
    elog_set_fmt(ELOG_LVL_ERROR, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_WARN, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_INFO, ELOG_FMT_LVL | ELOG_FMT_TAG | ELOG_FMT_TIME);
    elog_set_fmt(ELOG_LVL_DEBUG, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
    elog_set_fmt(ELOG_LVL_VERBOSE, ELOG_FMT_ALL & ~ELOG_FMT_FUNC);
#ifdef ELOG_COLOR_ENABLE
    elog_set_text_color_enabled(true);
#endif
    /* start EasyLogger */
    elog_start();
}

/**/
static int CreatSock(int SockType)
{
    int sock = 0;
    unsigned char on = 1;
    /*creat sock*/
    switch(SockType)
    {
        case SOCK_STREAM:{
            sock = socket(AF_INET, SOCK_STREAM, 0);
            break;
        }
        case SOCK_DGRAM:{
            sock = socket(AF_INET, SOCK_DGRAM, 0);
            break;
        }
        default:{
            log_e("not support socket type: %d", SockType);
            return FAIL;
        }
    }
    if(sock < 0)
    {
        log_e("creat rtsp sock error");
        return FAIL;
    }
    /*reuse sock*/
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on));
    return sock;
}

static int BindSockAddr(int sockfd, const char* ip, int port)
{
    struct sockaddr_in addr;

    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip);

    if(bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) < 0)
    {
        return FAIL;
    }

    return SUCCESS;
}

unsigned char* getoneline(unsigned char* buf, unsigned char* line)
{
    if(*buf =='\0' || *buf == '\r' )
    {
        return NULL;
    }

    while (*buf != '\0' && *buf != '\n')
    {
        *line = *buf;
        buf++;
        line++;
    }

    /*trans line to string and switch to next line */
    if(*buf == '\n')
    {
        *line = '\0';
        buf++;
    }

    return buf;
}

static int getmethod(unsigned char* bufptr, unsigned char* line,  unsigned char* method, unsigned char* url, unsigned char* version)
{
    int ret = 0;
    bufptr =  getoneline(bufptr, line);
    if(NULL == bufptr)
    {
        log_e("no line find in read buf");
        return FAIL;
    }
    ret = sscanf(line, "%s %s %s", method, url, version);
    if(ret != 3)
    {
        log_e("parase method fail");
    }
    return SUCCESS;
}

static int getcseq(unsigned char* buf, unsigned char* line, int* cseq)
{
    while(1)
    {
        /*parase <cseq>*/
        buf =  getoneline(buf, line);
        if(NULL == buf)
        {
            log_e("no line find in read buf");
            return FAIL;
        }
        if(sscanf(line, "CSeq: %d", cseq) == 1)
        {
            break;
        }
    }
    return SUCCESS;
}

static int gettranport(unsigned char* buf, unsigned char* line, unsigned char* contype, unsigned char* tranmod, int* rtpport, int* rtcpport)
{
    while(1)
    {
        /*parase <port>*/
        buf =  getoneline(buf, line);
        if(NULL == buf)
        {
            log_e("no line find in read buf");
            return FAIL;
        }
        if(sscanf(line,"Transport: %[^;];%[^;];client_port=%d-%d", contype, tranmod, rtpport, rtcpport) == 4)
        {
            break;
        }
    }
    return SUCCESS;
}

void  OptionHandle(unsigned char* reply, int cseq)
{
    sprintf(reply,"RTSP/1.0 200 OK \r\n"
                  "CSeq: %d\r\n"
                  "Public: OPTIONS, DESCRIBE, SETUP, PLAY\r\n"
                  "\r\n",
                  cseq
    );
}

static int  DescribeHandle(unsigned char* reply, int cseq, unsigned char* url)
{
    unsigned char IP[50] = { 0 };
    unsigned char sdp[500] = { 0 };
    int ret = 0;

    /*check ptr*/
    if(NULL == reply)
    {
        log_e("NULL ptr: reply");
        return FAIL;
    }

    /*parase IP*/
    ret = sscanf(url, "rtsp://%[^:]:", IP);
    if(ret != 1)
    {
        log_e("parase IP err");
        return FAIL;
    }
    log_d("server ip:%s", IP);

    /*build sdp file*/
    sprintf(sdp, "v=0\r\n" 
                 "o=- 9%ld 1 IN IP4 %s\r\n"
                 "t=0 0\r\n"
                 "a=control:*\r\n"
                 "m=video 0 RTP/AVP 96\r\n"
                 "a=rtpmap:96 H264/90000\r\n"
                 "a=control:track0\r\n",
                 time(NULL),
                 IP);

    
    /*build message*/
    sprintf(reply, "RTSP/1.0 200 OK\r\n"
                   "Cseq: %d\r\n"
                   "Content-Base: %s\r\n"
                   "Content-type: application/sdp\r\n"
                   "Content-length: %d\r\n\r\n"
                   "%s",
                   cseq,
                   url,
                   strlen(sdp),
                   sdp);

    return SUCCESS;
}

void  SetupHandle(unsigned char* reply, unsigned char* connecttype, unsigned char* sendmode, int cseq, int rtpport, int rtcpport)
{
    sprintf(reply,  "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Session: %d\r\n"
                    "Transport: %s;%s;client_port=%d-%d;server_port=%d-%d\r\n"
                    "\r\n",
                    cseq,
                    DEFAULT_SESSION_ID,
                    connecttype,
                    sendmode,
                    rtpport,
                    rtcpport,
                    SERVER_RTP_PORT,
                    SERVER_RTCP_PORT);
}

void  PlayHandle(unsigned char* reply, int cseq)
{
    sprintf(reply,  "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Range: npt=0.000-\r\n"
                    "Session: %d;  timeout=60\r\n\r\n"
                    "\r\n",
                    cseq,
                    DEFAULT_SESSION_ID);                  
}

void  TearDownHandle(unsigned char* reply, int cseq)
{
    sprintf(reply, "RTSP/1.0 200 OK\r\n"
                    "CSeq: %d\r\n"
                    "Session: %d\r\n"
                    "\r\n",
                    cseq,
                    DEFAULT_SESSION_ID);
}

unsigned int RtpHeadInit(RtpHeader* PRtpHeader)
{
    ELOG_ASSERT(PRtpHeader != NULL)

    memset(PRtpHeader, 0,sizeof(RtpHeader));
    PRtpHeader->Version = 2;
    PRtpHeader->Padding = 0;
    PRtpHeader->Extendion = 0;
    PRtpHeader->CsrcCount = 0;
    PRtpHeader->Mark = 0;               // 1bit
    PRtpHeader->PlayLoad = 96;          // 7bit
    PRtpHeader->Sequence = 0;           // 2byte  
    PRtpHeader->TimeStamp = 0;          // 4byte
    PRtpHeader->SSRC = rand()%10000;    // 4byte
    return SUCCESS;
}

unsigned int IsStartCode3(unsigned char* buf)
{
    ELOG_ASSERT(buf != NULL)

    if(buf[0] == 0 && buf[1] == 0 && buf[2] == 1)
    {
        return 1;
    }
    
    return 0;
}

unsigned int IsStartCode4(unsigned char* buf)
{
    ELOG_ASSERT(buf != NULL)
    {
        if(buf[0] == 0 && buf[1] == 0 && buf[2] == 0 && buf[3] == 1)
        {
            return 1;
        }
    }
    return 0;
}

unsigned int calnalusize(unsigned char* buf, int *nalsize)
{
    int pos = 0;
    for(pos = 0; pos < READ_BUF_SIZE - 4; pos++)
    {
        if(IsStartCode3(buf + pos))
        {
            *nalsize = pos;
            return SUCCESS;
        }
        if(IsStartCode4(buf + pos))
        {
            *nalsize = pos;
            return SUCCESS;
        }
    }
    return FAIL;
}

void AdjustTimeStamp(RtpHeader* PSRtpHeader, unsigned int NaluType)
{
    if(NaluType == 1 || NaluType == 5)
    {
        PSRtpHeader->TimeStamp += 90000 / FPS;
    }
}

void DumpData(void* dataptr, unsigned int size)
{
    unsigned char* c = dataptr;
    int i = 0;
    log_d("---------");
    for(i = 0; i < size; i++)
    {
        log_d("%X", *(c + i));
    }
    log_d("---------");
}


void SendMediaData(void *param)
{
    ELOG_ASSERT(param != NULL)

    SessionContext *scontext = NULL;
    struct sockaddr_in clientrtpaddr_in = { 0 };
    struct sockaddr clientrtpaddr = { 0 };
    RtpHeader SRtpHeader = {0};
    int ret = 0, filesize = 0, Nalsize = 0, nextcodeoffset = 0, rtpackcout = 0, loop = 0, rtpsendcount = 0;
    int FAUplayloadlen = 0, startcodelen = 0, rtppacketype = 0, count = 0;
    unsigned char readbuf[READ_BUF_SIZE] = {0};
    FILE* fph264file = NULL;
    NaluInfo SNlauInfo = { 0 };
    RtpSigPacket SRtpSigPacket = { 0 };
    RtpFU_APacket SRtpFU_APacket = { 0 };
    scontext = (SessionContext*)param;

    /*printf session info*/
    log_d("client information:---------------");
    log_d("Client IP: %s",inet_ntoa(scontext->ClientAddr.sin_addr));
    log_d("Request Url: %s", scontext->RequestUrl);
    log_d("Client Rtp Port: %d",scontext->ClientRtpPort);
    log_d("Client Rtcp Port: %d",scontext->ClientRtcpPort);
    log_d("Client Sock: %d",scontext->ClientSock);
    log_d("SerRtcpSock: %d",scontext->SerRtcpSock);
    log_d("SerRtpSock: %d",scontext->SerRtpSock);


    /*build client rtp addr*/
    clientrtpaddr_in.sin_family = AF_INET;
    clientrtpaddr_in.sin_port = htons(scontext->ClientRtpPort);
    clientrtpaddr_in.sin_addr.s_addr = inet_addr("192.168.93.133");//scontext->ClientAddr.sin_addr.s_addr;
    memcpy(&clientrtpaddr, &clientrtpaddr_in, sizeof( struct sockaddr));

    /*init rtp head*/
    ret = RtpHeadInit(&SRtpHeader);
    if(ret != SUCCESS)
    {
        log_e("rtpheader init fail");
        return;
    }

    /*open media file*/
    fph264file = fopen("new.h264","r");
    if(NULL == fph264file)
    {
        log_e("open file err: %s", ERRINFO);
        return;
    }
    fseek(fph264file, 0, SEEK_END);
    filesize = ftell(fph264file);
    if(filesize == 0)
    {
        log_e("file empty");
        return;
    }
    fseek(fph264file, 0, SEEK_SET);
    /*start trans media*/
    while(1)
    {
        /*read media data*/
        ret = fread(readbuf, sizeof(unsigned char), READ_BUF_SIZE, fph264file);
        if(ret != READ_BUF_SIZE)
        {
            if(ferror(readbuf) && !feof(readbuf))
            {
                log_e("read file err: %s", ERRINFO);
                return;
            }
            log_e("trans file complete");
            return;
        }
        /*calculate nalu size: nalu header + nalu data*/
        if(IsStartCode3(readbuf))
        {
            startcodelen = 3;
            ret = calnalusize(readbuf + 3, &Nalsize);       /*risk*/
            nextcodeoffset = Nalsize + 3;
            ELOG_ASSERT(ret == SUCCESS)
        }
        else if(IsStartCode4(readbuf))
        {
            startcodelen = 4;
            ret = calnalusize(readbuf + 4, &Nalsize);
            nextcodeoffset = Nalsize + 4;
            ELOG_ASSERT(ret == SUCCESS)
        }
        else
        {
            log_e("file format err");
        }
        /*set fileptr pos */
        fseek(fph264file, nextcodeoffset - READ_BUF_SIZE, SEEK_CUR);
        /*build nalu info*/
        BuildNaluInfo(readbuf + startcodelen, Nalsize, &SNlauInfo);
        log_d("--------------------------------------------");
        log_d("Nalu Length: %d", Nalsize);
        log_d("Nalu Type: %d", SNlauInfo.PSNaluHeader.NaluType);
        //ELOG_ASSERT(1 == 0)
        /*decision rtp packet type*/
        if(SNlauInfo.NaluLength < MAX_RTP_PACKET_LENGTH)
        {
            rtppacketype = 0;

            SRtpSigPacket.SRtpHeader = SRtpHeader;
            SRtpSigPacket.SRtpHeader.Sequence = htons(SRtpHeader.Sequence);
            SRtpSigPacket.SRtpHeader.TimeStamp = htonl(SRtpHeader.TimeStamp);
            SRtpSigPacket.SRtpHeader.SSRC = htonl(SRtpHeader.SSRC);

            SRtpSigPacket.SNaluHeader = SNlauInfo.PSNaluHeader;
            memcpy(SRtpSigPacket.NaluData, SNlauInfo.NaluData, SNlauInfo.NaluLength - 1); 
            rtpsendcount = sizeof(SRtpSigPacket.SRtpHeader) + sizeof(SRtpSigPacket.SNaluHeader) + SNlauInfo.NaluLength - 1;
            ret = sendto(scontext->SerRtpSock, &SRtpSigPacket, rtpsendcount, 0, &clientrtpaddr, sizeof(struct sockaddr));
            SRtpHeader.Sequence++;
            log_d("Rtp Package Type: Single, Length: %d", rtpsendcount);
            ELOG_ASSERT(ret == rtpsendcount)
        }
        else
        {
            rtppacketype = 1;
            rtpackcout = (SNlauInfo.NaluLength - 2) / MAX_RTP_PACKET_LENGTH + 1;
            for(loop = 0; loop < rtpackcout; loop++)
            {
                SRtpFU_APacket.SRtpHeader = SRtpHeader;
                SRtpFU_APacket.SRtpHeader.Sequence = htons(SRtpHeader.Sequence);
                SRtpFU_APacket.SRtpHeader.TimeStamp = htonl(SRtpHeader.TimeStamp);
                SRtpFU_APacket.SRtpHeader.SSRC = htonl(SRtpHeader.SSRC);

                SRtpFU_APacket.SFuIndication.F = SNlauInfo.PSNaluHeader.F;
                SRtpFU_APacket.SFuIndication.NRI = SNlauInfo.PSNaluHeader.NRI;
                SRtpFU_APacket.SFuIndication.PacketType = 28;

                SRtpFU_APacket.SFuHeader.S =  loop == 0 ? 1 : 0;
                SRtpFU_APacket.SFuHeader.E =  loop == (rtpackcout - 1) ? 1 : 0;
                SRtpFU_APacket.SFuHeader.R = 0;
                SRtpFU_APacket.SFuHeader.NaluType =  SNlauInfo.PSNaluHeader.NaluType;

                FAUplayloadlen = (loop == rtpackcout - 1) ? (SNlauInfo.NaluLength - 1) % MAX_RTP_PACKET_LENGTH : MAX_RTP_PACKET_LENGTH;
                memcpy(SRtpFU_APacket.NaluData, SNlauInfo.NaluData, FAUplayloadlen); 
                SNlauInfo.NaluData += FAUplayloadlen;
                rtpsendcount = sizeof(SRtpFU_APacket.SRtpHeader) + sizeof(SRtpFU_APacket.SFuIndication) + sizeof( SRtpFU_APacket.SFuHeader) + FAUplayloadlen;
                ret = sendto(scontext->SerRtpSock, &SRtpFU_APacket, rtpsendcount, 0, &clientrtpaddr, sizeof(struct sockaddr));
                SRtpHeader.Sequence++;
                log_d("Rtp Package Type: FU-A, Length: %d", rtpsendcount);
                ELOG_ASSERT(ret == rtpsendcount)
            }
        }
        AdjustTimeStamp(&SRtpHeader, SNlauInfo.PSNaluHeader.NaluType);
        //if(rtppacketype == 0) DumpData(&SRtpSigPacket, 30);
        usleep(1000000 / FPS);
    }
}


static int BuildNewContext(SessionContext **ppSeiContex)
{
    SessionContext* context = (SessionContext *)malloc(sizeof(SessionContext));
    if(NULL == context)
    {
        return FAIL;
    }
    memset(context, 0, sizeof(SessionContext));
    *ppSeiContex = context;
    return SUCCESS;
}


static int doclient(int SerRtpSock, int SerRtcpSock, int ClientSock, struct sockaddr_in clientaddr)
{
    unsigned char* readbuf = NULL;
    unsigned char* line = NULL;
    unsigned char* reply = NULL;
    unsigned char method[20] = {0};
    unsigned char url[100] = {0};
    unsigned char version[30] = {0};
    unsigned char contype[50] = {0};
    unsigned char tranmod[50] = {0};
    int msglength = 0, cseq = 0, ret = 0, rtpport = 0, rtcpport = 0, curcseq = 0, play = 0;

    /*malloc and clear readbuf*/
    readbuf = (unsigned char*)malloc(READ_BUF_SIZE);
    line = (unsigned char*)malloc(READ_BUF_SIZE);
    reply = (unsigned char*)malloc(READ_BUF_SIZE);
    if(NULL == readbuf || NULL == line || NULL == reply)
    {
        log_e("malloc buf fail :%s",strerror(errno));
    }
    memset(readbuf, 0, READ_BUF_SIZE);
    memset(line, 0, READ_BUF_SIZE);
    memset(reply, 0, READ_BUF_SIZE);

    while(1)
    {
        play = 0;
        msglength = recv(ClientSock,readbuf,READ_BUF_SIZE,0);
        if(msglength <= 0 )
        {
            log_e("read message faile :%s",strerror(errno));
            return FAIL;
        }
        /*transform message to string*/
        readbuf[msglength] = '\0'; 
        log_d("recive message from client C ---------> S");
        log_d("%s", readbuf);

        /*parase <method> <url> <version>*/
        ret = getmethod(readbuf, line, method, url, version);
        if(ret != SUCCESS)
        {
            log_e("parase method error");
        }
        log_d("method: %s url: %s version: %s", method, url, version);

        /*parase <cseq>*/
        ret = getcseq(readbuf, line, &cseq);
        if(ret != SUCCESS)
        {
            log_e("get cseq error");
            return FAIL;
        }
        log_d("cseq: %d", cseq);

        /*Process client request*/
        if(strcmp(method, "OPTIONS") == 0)
        {
            curcseq = cseq;
            OptionHandle(reply, cseq);
            goto SendPoint;
        }

        /*describe*/
        if(strcmp(method, "DESCRIBE") == 0)
        {
            if(curcseq != cseq)
            {
                log_w("DESCRIBE sequence error: %s", cseq);
                return FAIL;
            }
            DescribeHandle(reply, cseq, url);
            goto SendPoint;
        }

        /*set up*/
        if(strcmp(method, "SETUP") == 0)
        {
            if(curcseq != cseq)
            {
                log_w("SETUP sequence error: %s", cseq);
                return FAIL;
            }
            /*parase trans port*/
            ret = gettranport(readbuf, line, contype, tranmod, &rtpport, &rtcpport);
            if(ret != SUCCESS)
            {
                log_e("parase tran port err");
                return FAIL;
            }
            log_d("connect type: %s, tranmod: %s, rtp port: %d, rtcp port: %d", contype, tranmod, rtpport, rtcpport);

            SetupHandle(reply, contype, tranmod, cseq, rtpport, rtcpport);
            goto SendPoint;
        }

        /*play*/
        if(strcmp(method, "PLAY") == 0)
        {
            if(curcseq != cseq)
            {
                log_w("PLAY sequence error: %s", cseq);
                return FAIL;
            }
            PlayHandle(reply, cseq);
            play = 1;
            goto SendPoint;
        }

        /*teardown*/
        if(strcmp(method, "TEARDOWN") == 0)
        {
            TearDownHandle(reply, cseq);
            goto SendPoint;
        }

        log_e("not suport this option: %s", method);
        return FAIL;

SendPoint:
        curcseq++;
        log_d("respond message to client: S-------------->C");
        log_d("%s", reply);

        ret = send(ClientSock, reply, strlen(reply), 0);
        if(ret != strlen(reply))
        {
            log_e("send message to client fail");
            return FAIL;
        }
        if(play)
        {
            SessionContext *context = NULL;
            ret = BuildNewContext(&context);
            if(ret != SUCCESS)
            {
                log_e("build session context err");
            }
            if(context == NULL)
            {
                log_e("context null");
            }
            /*trans client context*/
            strcpy(context->RequestUrl, url);
            context->ClientAddr = clientaddr;
            context->ClientSock = ClientSock;
            context->SerRtpSock = SerRtpSock;
            context->SerRtcpSock = SerRtcpSock;
            context->ClientRtpPort = rtpport;
            context->ClientRtcpPort = rtcpport;
            ret = pthread_create(&sendthread,NULL,SendMediaData, context);
            if(ret != 0)
            {
                log_e("creat thread fail");
            }
        }
    }
}

void ParaseTest(void)
{
    unsigned char* readbuf = NULL;
    unsigned char* line = NULL;
    unsigned char* reply = NULL;
    unsigned char method[20] = {0};
    unsigned char url[100] = {0};
    unsigned char version[30] = {0};
    unsigned char contype[50] = {0};
    unsigned char sendmod[50] = {0};
    int msglength = 0, cseq = 0, ret = 0, rtpport = 0, rtcpport = 0;

    /*malloc and clear readbuf*/
    readbuf = (unsigned char*)malloc(READ_BUF_SIZE);
    line = (unsigned char*)malloc(READ_BUF_SIZE);
    reply = (unsigned char*)malloc(READ_BUF_SIZE);
    if(NULL == readbuf || NULL == line || NULL == reply)
    {
        log_e("malloc buf fail :%s",strerror(errno));
    }
    memset(readbuf, 0, READ_BUF_SIZE);
    memset(line, 0, READ_BUF_SIZE);
    memset(reply, 0, READ_BUF_SIZE);
    
    strcpy(readbuf,"OPTIONS rtsp://192.168.93.133:8554/live/simis RTSP/1.0\r\nCSeq: 2\r\nUser-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)");
    ret = getmethod(readbuf, line, method, url, version);
    if(ret != SUCCESS)
    {
        log_e("parase method err");
        return;
    }
    log_d("method: %s url: %s version: %s", method, url, version);
    ret = getcseq(readbuf, line, &cseq);
    if(ret != SUCCESS)
    {
        log_e("parase cseq err");
        return;
    }
    log_d("cseq: %d", cseq);

    strcpy(readbuf,"SETUP rtsp://127.0.0.1:8554/live/track0 RTSP/1.0\r\nCSeq: 4\r\nTransport: RTP/AVP;unicast;client_port=54492-54493\r\n\r\n");
    ret = gettranport(readbuf, line,contype,sendmod, &rtpport, &rtcpport);
    if(ret != SUCCESS)
    {
        log_e("parase port err");
        return;
    }
    log_d("connect type: %s send mode: %s rtpport: %d, rtcpport: %d", contype, sendmod, rtpport, rtcpport);

}



int main(void) 
{
    int ret = 0, rtspsock = -1, rtpsock = -1, rtcpsock = -1, clientsock = -1;
    struct sockaddr clientaddr = { 0 };
    struct sockaddr_in clientaddr_ = { 0 };
    socklen_t addrlen = 0;

    /*init log module*/
    LogModuleInit();

    //ParaseTest();

    /*creat sockt*/
    rtspsock = CreatSock(SOCK_STREAM);
    rtpsock  = CreatSock(SOCK_DGRAM);
    rtcpsock = CreatSock(SOCK_DGRAM);
    if(rtspsock < 0 || rtpsock < 0 || rtcpsock < 0 )
    {
        log_e("creat sock error");
        return EXIT_FAILURE;
    }
    log_d("rtspsock: %d, rtpsock: %d, rtcpsock: %d ", rtspsock, rtpsock, rtcpsock);

    /*bind server addr*/
    ret = BindSockAddr(rtspsock, "0.0.0.0", SERVERPORT);
    ret = BindSockAddr(rtpsock,"0.0.0.0",SERVER_RTP_PORT);
    ret = BindSockAddr(rtcpsock,"0.0.0.0",SERVER_RTCP_PORT);
    if(ret != 0)
    {
        log_e("bind sock addr error");
        return EXIT_FAILURE;
    }
    log_d("set server addr: 0.0.0.0, port: %d", SERVERPORT);

    /*set server listening*/
    ret = listen(rtspsock, 5);
    if(ret != 0)
    {
        log_e("set sock listen error");
        return EXIT_FAILURE;
    }
    log_d("server listening...");

    /*accept client connect*/
    while(1)
    {
        clientsock = accept(rtspsock, &clientaddr, &addrlen);
        /*trans addr format*/
        memcpy(&clientaddr_, &clientaddr, sizeof(clientaddr));
        doclient(rtpsock, rtcpsock, clientsock, clientaddr_);
    }

    return EXIT_SUCCESS;
}


